Link
Description
给定 n,m,k,对于所有的 0≤i≤n,0≤j≤min(i,m) 有多少对 (i,j) 满足 Cijmodk=0
1≤n,m≤1018, 1≤k≤100 且 k 为质数。
Soluton
数位dp+Lucas定理
因为 k 是质数,所以 Cnmmodk≡Cnmodkmmodk×Cn/km/kmodk
又因为当 n<m 时,Cnm=0,就合法了。
因此我们只需要在数位dp时,记一下是否存在一位 ni<mi。
这里数位dp时需要同时处理两个数,第二个数的边界也需要特殊处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #define ll long long
using namespace std;
const int p = 1e9 + 7; ll n, m, k; int a[100], b[100], len; ll f[100][2][2][2][2];
void init() { scanf("%lld%lld", &n, &m); ll mx = max(n, m); len = 0; while(mx) len++, mx /= k; memset(a, 0, sizeof(a)); for(int i = 1; i <= len; i++) a[i] = n % k, n /= k; memset(b, 0, sizeof(b)); for(int i = 1; i <= len; i++) b[i] = m % k, m /= k; memset(f, -1, sizeof(f)); }
ll dfs(int cur, bool lima, bool limb, bool cmp, bool flag) { if(~f[cur][lima][limb][cmp][flag]) return f[cur][lima][limb][cmp][flag]; if(!cur) return flag; int upa = lima ? a[cur] : k - 1, upb = limb ? b[cur] : k - 1; ll res = 0; for(int i = 0; i <= upa; i++) for(int j = 0; j <= (cmp ? upb : min(i, upb)); j++) res = (res + dfs(cur - 1, lima & (i == upa), limb & (j == upb), cmp | (i > j), flag | (i < j))) % p; return f[cur][lima][limb][cmp][flag] = res; }
int main() { int T; scanf("%d%lld", &T, &k); while(T--) { init(); printf("%lld\n", dfs(len, 1, 1, 0, 0)); } return 0; }
|